package dao

import (
	"context"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/spf13/viper"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/schema"
	"gorm.io/plugin/dbresolver"
	"strings"
	"time"
)

var _db *gorm.DB

// 链接mysql
func initMysql() {
	//读 (8) 写 (2)分离 - 主(写)从(读)复制配置 //如果是正式项目，是mysql集群
	//dsn := "root:root@tcp(127.0.0.1:3306)/gostudy?charset=utf8mb4&parseTime=True&loc=Local"

	////读数据库的链接
	//readDsn := strings.Join([]string{conf.AppConf.MysqlReadConf.User, ":", conf.AppConf.MysqlReadConf.Psd, "@tcp(", conf.AppConf.MysqlReadConf.Host, ":", strconv.Itoa(conf.AppConf.MysqlReadConf.Port), ")/", conf.AppConf.MysqlReadConf.Dbname, "?charset=", conf.AppConf.MysqlReadConf.Charset, "&parseTime=True&loc=Local"}, "")
	////写数据库的链接
	//writeDsn := strings.Join([]string{conf.AppConf.MysqlWriteConf.User, ":", conf.AppConf.MysqlWriteConf.Psd, "@tcp(", conf.AppConf.MysqlWriteConf.Host, ":", strconv.Itoa(conf.AppConf.MysqlWriteConf.Port), ")/", conf.AppConf.MysqlWriteConf.Dbname, "?charset=", conf.AppConf.MysqlWriteConf.Charset, "&parseTime=True&loc=Local"}, "")
	//

	//读数据库的链接
	readDsn := strings.Join([]string{viper.Get("mysql_read.user").(string), ":", viper.Get("mysql_read.psd").(string), "@tcp(", viper.Get("mysql_read.host").(string), ":", viper.Get("mysql_read.port").(string), ")/", viper.Get("mysql_read.dbname").(string), "?charset=", viper.Get("mysql_read.charset").(string), "&parseTime=True&loc=Local"}, "")
	//写数据库的链接
	writeDsn := strings.Join([]string{viper.Get("mysql_write.user").(string), ":", viper.Get("mysql_write.psd").(string), "@tcp(", viper.Get("mysql_write.host").(string), ":", viper.Get("mysql_write.port").(string), ")/", viper.Get("mysql_write.dbname").(string), "?charset=", viper.Get("mysql_write.charset").(string), "&parseTime=True&loc=Local"}, "")

	//gorm v2.0版本的内置日志打印
	var ormLogger logger.Interface
	//如果gin的模式是 debug 模式
	if gin.Mode() == "debug" {
		//打印日志信息
		ormLogger = logger.Default.LogMode(logger.Info)
	} else {
		ormLogger = logger.Default
	}

	//链接mysql
	gormDB, err := gorm.Open(mysql.New(mysql.Config{
		DSN:               writeDsn, //主数据库 // DSN data source name ,这一行是必须得用的 主数据库
		DefaultStringSize: 256,      //utf8mb4 string 类型字段的默认长度 ; utf8的string 类型字段的默认长度是256
		//	DisableDatetimePrecision:  true,     // 禁用 datetime 精度，MySQL 5.6 之前的数据库不支持
		//	DontSupportRenameIndex:    true,    //重命名索引时采用先删除并新建的方式，MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引(即alter table 表名 rename to 新的表名)
		//	DontSupportRenameColumn:   true,      // 用 `change` 重命名列，MySQL 8 之前的数据库和 MariaDB 不支持重命名列(命令行那个alter table 表名 change 列名 新列别 新数据类型;)
		SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
	}), &gorm.Config{
		Logger: ormLogger, //orm日志

		//禁止创建外键  //设置成为逻辑外键(在物理数据库上没有外键，仅体现在代码上)
		//DisableForeignKeyConstraintWhenMigrating: true,
		//SkipDefaultTransaction: false, //跳过默认事务
		//命名策略
		NamingStrategy: schema.NamingStrategy{
			TablePrefix:   "go_", // 表前缀， table name prefix, table for `User` would be `t_users`
			SingularTable: true,  // 表明不加s, use singular table name, table for `User` would be `user` with this option enabled
			//NoLowerCase: true, // skip the snake_casing of names
			//NameReplacer: strings.NewReplacer("CID", "Cid"), // use name replacer to change struct/field name before convert it to db name
		},
	})
	if err != nil {
		fmt.Println("start mysql is err:", err.Error())
		return
	}

	sqlDB, err := gormDB.DB()
	if err != nil {
		fmt.Println(err)
	}

	// SetMaxIdleConns 设置空闲连接池中连接的最大数量
	sqlDB.SetMaxIdleConns(20)

	// SetMaxOpenConns 设置打开数据库连接的最大数量。
	sqlDB.SetMaxOpenConns(100)

	// SetConnMaxLifetime 设置了连接可复用的最大时间。
	sqlDB.SetConnMaxLifetime(time.Second * 30) //time.Hour

	_db = gormDB

	//主从配置  需要用到插件  go get gorm.io/plugin/dbresolver
	_ = _db.Use(dbresolver.
		Register(dbresolver.Config{
			// `db2` 作为 sources，`db3`、`db4` 作为 replicas
			//Replication是用于将数据从一台mysql（source）复制到另外一台或者多台mysql(replicas)，Replication同步默认是asynchronous（异步的），可以配置同步所有数据库，一个数据库，或者只是同步一张表。
			//Sources:  []gorm.Dialector{mysql.Open("db2_dsn")},
			//Replicas: []gorm.Dialector{mysql.Open("db3_dsn"), mysql.Open("db4_dsn")},

			//Sources  主表 写    Replicas  从表 读
			Sources:  []gorm.Dialector{mysql.Open(writeDsn)},                     //主写
			Replicas: []gorm.Dialector{mysql.Open(readDsn), mysql.Open(readDsn)}, //从读
			// sources/replicas 负载均衡策略
			Policy: dbresolver.RandomPolicy{},
		}))
	//除了数据库，还可以单独表  查看手册https://gorm.io/zh_CN/docs/dbresolver.html
	//.Register(dbresolver.Config{
	//	// `db1` 作为 sources（DB 的默认连接），对于 `User`、`Address` 使用 `db5` 作为 replicas
	//	Replicas: []gorm.Dialector{mysql.Open("db5_dsn")},
	//	}, &User{}, &Address{}).Register(dbresolver.Config{
	//		// `db6`、`db7` 作为 sources，对于 `orders`、`Product` 使用 `db8` 作为 replicas
	//		Sources:  []gorm.Dialector{mysql.Open("db6_dsn"), mysql.Open("db7_dsn")},
	//		Replicas: []gorm.Dialector{mysql.Open("db8_dsn")},
	//	}, "orders", &Product{}, "secondary"))

	//数据库的迁移,即绑定并生成数据表 https://gorm.io/zh_CN/docs/migration.html
	//企业里面不用这个，因为表已经建好了！！！
	//没有建表的话，也不用这个，否则DBA(数据库管理员)肯定会找你麻烦的
	//就自己练习用这个
	//migration()

}

// NewDBClient 新建一个db链接    虽然gin的context 与 go的context不一样，但是可以联通的
// 文档：https://www.bookstack.cn/read/gorm-1.23.5-zh/6308046bcc8c2fbe.md
// 持续会话模式  持续会话模式通常被用于执行一系列操作。
func NewDBClient(ctx context.Context) *gorm.DB {
	db := _db
	return db.WithContext(ctx)
}

func InitMysql() {
	initMysql()
}
